﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibNiconico.NiconicoApi;
using System.Diagnostics;
using NicoView.Model.Speech;
using NicoView.Model.Service.Comments;
using NicoView.Model.Service.Events;
using LibNiconico.NiconicoApi.Lives;
using LibNiconico.NiconicoApi.Lives.Comments;
using LibNiconico.NiconicoApi.Lives.WayBackKey;
using LibNiconico.NiconicoApi.HeartBeat;
using NicoView.Model.Service.Results;

namespace NicoView.Model.Service.Lives
{
    public sealed class NicoLiveCommentClient : NicoCommentClientBase
    {
        private INicoLiveCommentClient commentClient;

        private NicoLiveService service;

        public override NicoServiceBase Service
        {
            get { return this.service; }
        }

        private NicoLiveCommentClientInfo _info;

        public override event NicoLiveHeartBeatHandler HeartBeatReceived;

        public event NicoLiveCloseHandler Closed;

        /// <summary>
        /// ハートビート受信イベントハンドラ
        /// </summary>
        /// <param name="args"></param>
        public delegate void NicoLiveHeartBeatHandler(NicoLiveHeartBeatReceivedEventArgs args);

        /// <summary>
        /// 生放送が閉じられた場合に呼び出されるイベントハンドラ
        /// </summary>
        /// <param name="args"></param>
        public delegate void NicoLiveCloseHandler(NicoLiveCommentCloseEventArgs args);

        internal NicoLiveCommentClient(NicoLiveService service, INicoLiveCommentClient commentClient)
        {
            this.commentClient = commentClient;

           
            
            this.comments = new List<NotifyNicoComment>();

            commentClient.ChatReceived += onReadChat;

            commentClient.ReadException += onException;
            commentClient.ThreadReceived += onThread;
            commentClient.HeartBeatClient.Received += onHeartBeat;
            commentClient.ChatResultReceived += onReadChatResult;
            commentClient.Closed += onClose;
            this.service = service;
            _info = new NicoLiveCommentClientInfo();

            InternalColors.Add(NicoCommentColrs.White);
            InternalColors.Add(NicoCommentColrs.Red);
            InternalColors.Add(NicoCommentColrs.Pink);
            InternalColors.Add(NicoCommentColrs.Orange);
            InternalColors.Add(NicoCommentColrs.Yellow);
            InternalColors.Add(NicoCommentColrs.Green);
            InternalColors.Add(NicoCommentColrs.Cyan);
            InternalColors.Add(NicoCommentColrs.Blue);
            InternalColors.Add(NicoCommentColrs.Purple);
            InternalColors.Add(NicoCommentColrs.Black);

            InternalPositions.Add(NicoCommentPositions.Center);
            InternalPositions.Add(NicoCommentPositions.Top);
            InternalPositions.Add(NicoCommentPositions.Under);

            InternalSizings.Add(NicoCommentSizings.Medium);
            InternalSizings.Add(NicoCommentSizings.Big);
            InternalSizings.Add(NicoCommentSizings.Small);

            if (service.LiveServiceInfo.IsPremium)
            {
                InternalColors.Add(NicoCommentColrs.White2);
                InternalColors.Add(NicoCommentColrs.Red2);
                InternalColors.Add(NicoCommentColrs.Pink2);
                InternalColors.Add(NicoCommentColrs.Orange2);
                InternalColors.Add(NicoCommentColrs.Yellow2);
                InternalColors.Add(NicoCommentColrs.Green2);
                InternalColors.Add(NicoCommentColrs.Cyan2);
                InternalColors.Add(NicoCommentColrs.Blue2);
                InternalColors.Add(NicoCommentColrs.Purple2);
            }


        }

        private void onReadChatResult(NicoLiveCommentClientChatResultCommentEventArgs args)
        {
            OnNicoCommentChatResult(new NicoCommentSentResultEventArgs(args.ChatResult, this));
        }

        private void onClose(NicoLiveCommentClientCloseEventArgs args)
        {
            if (Closed != null) Closed(new NicoLiveCommentCloseEventArgs(args.Exception));
        }

        private void onHeartBeat(NicoHaertBeatReceivedEventArgs args)
        {
            if (HeartBeatReceived != null) HeartBeatReceived(new NicoLiveHeartBeatReceivedEventArgs(args));
        }

        private void onThread(NicoLiveCommentClientThreadCommentEventArgs args)
        {
            if (_info.ThreadConnectionStartTime == default(DateTime)) _info.ThreadConnectionStartTime = args.ThreadComment.ServerTime;
            OnNicoCommentThreadReceived(new NicoCommentThreadEventArgs(args.ThreadComment.ServerTime));
        }

        private void onException(NicoLiveReadExceptionEventArgs args)
        {
            throw new NicoException(args.Exception);
        }

        private void onPeriod(int  chatCount)
        {
            this.OnNicoCommentReceivePeriod(new Events.NicoCommentReceivePeriodEventArgs(chatCount, this));
        }

        private  void onReadChat(NicoLiveCommentClientChatCommentEventArgs args)
        {
            var chatComments = new List<NotifyNicoComment>();
            foreach (var chatComment in args.ChatComments)
            {
                var requestArgs = new Events.NicoCommentGetUserRequestEventArgs(chatComment.UserId);
                this.OnNicoCommentGetUserRequest(requestArgs);
                chatComments.Add(NotifyNicoComment.Create(chatComment, requestArgs.User, service.LiveServiceInfo, this._info, this));
            }


            comments.AddRange(chatComments);
            this.OnNicoCommentReceive(new Events.NicoCommentReceiveEventArgs(chatComments, this));
            
            
           
        }

        private List<NotifyNicoComment> comments;

        internal async Task ConnectAsync()
        {
            await commentClient.ConnectAsync();

        }

        public override int CalcActiveWatchCount(TimeSpan elapsed)
        {
            var calcStartTime = this.service.LiveServiceInfo.StartTime + elapsed - TimeSpan.FromMinutes(10);
            var activeUsers = new HashSet<NotificationNicoUser>();

            foreach(var comment in this.Comments)
            {
                if (comment.Date > calcStartTime && !activeUsers.Contains(comment.User))
                {
                    activeUsers.Add(comment.User);
                }
            }

            return activeUsers.Count;
        }

        protected override async Task<IResult> OnSendAsync(NicoSendComment comment)
        {

            if (comment.IsOwner)
            {

                var result = await commentClient.SendOwnerChatCommentAsync(comment.AsLiveOwnerSubmitComment()).ConfigureAwait(false);

                return new Result(result);

            }
            else
            {
                await commentClient.SendAsync(comment.AsLiveSubmitComment()).ConfigureAwait(false);
            }
            return new Result(true);
        }

        internal async Task<IResult> SendDisconnectComment()
        {
            var comment = new NicoSendComment();
            comment.IsOwner = true;
            comment.IsAnonymous = true;
            comment.Body = "/disconnect";

            return await this.SendAsync(comment).ConfigureAwait(false);
        }


        public override async Task WayBacRequestkAsync(int count)
        {
            await commentClient.SendWayBackRequest(new NicoLiveGetWayBackRequestQueryInfo() { ResFrom = count });
        }


        public override void Dispose()
        {
            
            commentClient.Dispose();

        }


        public override ICollection<NotifyNicoComment>  Comments
        {
            get { return comments; }
        }





        public override INicoCommentClientInfo Info
        {
            get { return _info; }
        }



        public override bool Connected
        {
            get { return this.commentClient != null && this.commentClient.Connected; }
        }
    }
}
